package com.cars.elasticsearch.util.elasticSearch;

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch.core.*;
import co.elastic.clients.elasticsearch.core.bulk.BulkResponseItem;
import co.elastic.clients.elasticsearch.core.bulk.UpdateAction;
import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.transport.endpoints.BooleanResponse;
import com.cars.elasticsearch.domain.DO.Course;
import com.cars.elasticsearch.exception.EsException;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * @author: caoqianfan
 * @date: 2023/10/13 9:47
 */
@Slf4j
@Component
public class DocumentUtil {

    @Resource
    private ElasticsearchClient client;

    @Resource
    private IndexUtil indexUtil;

    /**
     * 判断document是否存在.
     *
     * @param indexName index名称
     */
    public boolean existDocument(Course course, String indexName) {
        if (!indexUtil.existIndex(indexName)) {
            indexUtil.createIndex(indexName);
        }
        try {
            BooleanResponse booleanResponse = client.exists(e -> e.index(indexName).id(course.getId().toString()));
            log.info(booleanResponse.toString());
            return booleanResponse.value();
        } catch (IOException e) {
            throw new EsException("在es中检测索引【" + indexName + "】 documentID:" + course.getId() + " 出错，错误信息为：" + e.getMessage());
        }
    }

    /**
     * 插入document.
     *
     * @param course    课程
     * @param indexName index名称
     */
    public void addDocument(Course course, String indexName) {
        if (this.existDocument(course, indexName)) {
            throw new EsException("es中已存在documentID:" + course.getId());
        }
        try {
            IndexResponse indexResponse = client.index(i -> i.index(indexName).id(course.getId().toString()).document(course));
            log.info(indexResponse.toString());
        } catch (IOException e) {
            throw new EsException("在es中插入document出错，错误信息为：" + e.getMessage());
        }
    }

    /**
     * 删除document.
     *
     * @param course    课程
     * @param indexName index名称
     */
    public void deleteDocument(Course course, String indexName) {
        if (!this.existDocument(course, indexName)) {
            throw new EsException("es中不存在documentID:" + course.getId());
        }
        try {
            DeleteResponse deleteResponse = client.delete(d -> d
                    .index(indexName)
                    .id(course.getId().toString())
            );
            log.info(deleteResponse.toString());
        } catch (IOException e) {
            throw new EsException("在es中删除document出错，错误信息为：" + e.getMessage());
        }
    }

    /**
     * 更新document.
     *
     * @param course    课程
     * @param indexName index名称
     */
    public void updateDocument(Course course, String indexName) {
        if (!this.existDocument(course, indexName)) {
            throw new EsException("es中不存在documentID:" + course.getId());
        }
        try {
            UpdateResponse<Course> updateResponse = client.update(u -> u
                            .index(indexName)
                            .id(course.getId().toString())
                            .doc(course)
                    , Course.class);
            log.info(updateResponse.toString());
        } catch (IOException e) {
            throw new EsException("在es中更新document出错，错误信息为：" + e.getMessage());
        }
    }

    /**
     * 批量插入document.
     *
     * @param courseList 添加的数量集合
     * @param indexName  indexName
     */
    public void batchAddDocument(List<Course> courseList, String indexName) {

        List<String> existDocumentList = new ArrayList<>();

        BulkRequest.Builder br = new BulkRequest.Builder();
        courseList.forEach(course ->
                {
                    if (this.existDocument(course, indexName)) {
                        existDocumentList.add(course.getId().toString());
                    }
                    br.operations(op -> op.index(idx -> idx
                            .index(indexName)
                            .id(course.getId().toString())
                            .document(course)
                    ));
                }
        );

        if (!existDocumentList.isEmpty()) {
            throw new EsException("es中已存在以下documentID:" + existDocumentList);
        }

        try {
            BulkResponse result = client.bulk(br.build());
            if (result.errors()) {
                log.error("Bulk had errors");
                for (BulkResponseItem item : result.items()) {
                    if (item.error() != null) {
                        log.error(item.error().reason());
                    }
                }
            }
        } catch (IOException e) {
            throw new EsException("在es中批量插入document出错，错误信息为：" + e.getMessage());
        }
    }

    /**
     * 批量删除document.
     *
     * @param courseList 添加的数量集合
     * @param indexName  indexName
     */
    public void batchDeleteDocument(List<Course> courseList, String indexName) {

        List<String> notExistDocumentList = new ArrayList<>();

        BulkRequest.Builder br = new BulkRequest.Builder();
        courseList.forEach(course ->
                {
                    if (!this.existDocument(course, indexName)) {
                        notExistDocumentList.add(course.getId().toString());
                    }
                    br.operations(op -> op.delete(idx -> idx
                            .index(indexName)
                            .id(course.getId().toString())
                    ));
                }
        );

        if (!notExistDocumentList.isEmpty()) {
            throw new EsException("es中不存在以下documentID:" + notExistDocumentList);
        }

        try {
            BulkResponse result = client.bulk(br.build());
            if (result.errors()) {
                log.error("Bulk had errors");
                for (BulkResponseItem item : result.items()) {
                    if (item.error() != null) {
                        log.error(item.error().reason());
                    }
                }
            }
        } catch (IOException e) {
            throw new EsException("在es中批量删除document出错，错误信息为：" + e.getMessage());
        }
    }

    /**
     * 批量更新document.
     *
     * @param courseList 添加的数量集合
     * @param indexName  indexName
     */
    public void batchUpdateDocument(List<Course> courseList, String indexName) {

        List<String> notExistDocumentList = new ArrayList<>();

        BulkRequest.Builder br = new BulkRequest.Builder();
        courseList.forEach(course ->
                {
                    if (!this.existDocument(course, indexName)) {
                        notExistDocumentList.add(course.getId().toString());
                    }
                    br.operations(op -> op.update(idx -> idx
                            .index(indexName)
                            .id(course.getId().toString())
                            .action(new UpdateAction.Builder<>().doc(course).build())
                    ));
                }
        );

        if (!notExistDocumentList.isEmpty()) {
            throw new EsException("es中不存在以下documentID:" + notExistDocumentList);
        }

        try {
            BulkResponse result = client.bulk(br.build());
            if (result.errors()) {
                log.error("Bulk had errors");
                for (BulkResponseItem item : result.items()) {
                    if (item.error() != null) {
                        log.error(item.error().reason());
                    }
                }
            }
        } catch (IOException e) {
            throw new EsException("在es中批量删除document出错，错误信息为：" + e.getMessage());
        }
    }

    /**
     * 查询指定document.
     *
     * @param course    课程
     * @param indexName index名称
     */
    public Course getDocument(Course course, String indexName) {
        if (!this.existDocument(course, indexName)) {
            throw new EsException("es中不存在documentID:" + course.getId());
        }
        try {
            GetResponse<Course> getResponse = client.get(g -> g
                            .index(indexName)
                            .id(course.getId().toString())
                    , Course.class
            );
            log.info(getResponse.toString());
            return getResponse.source();
        } catch (IOException e) {
            throw new EsException("在es中查询document出错，错误信息为：" + e.getMessage());
        }
    }

    /**
     * 查询所有document.
     *
     * @param indexName index名称
     */
    public List<Course> listDocument(String indexName) {
        try {
            SearchResponse<Course> searchResponse = client.search(s -> s.index(indexName), Course.class);
            log.info(searchResponse.toString());
            List<Hit<Course>> hitList = searchResponse.hits().hits();
            List<Course> courseList = new ArrayList<>();
            for (Hit<Course> mapHit : hitList) {
                courseList.add(mapHit.source());
            }
            return courseList;
        } catch (IOException e) {
            throw new EsException("在es中查询document出错，错误信息为：" + e.getMessage());
        }
    }

    /**
     * 根据索引名称和字段查询数据.
     *
     * @param indexName  索引名称
     * @param filedValue 查询字段值
     * @param filedName  查询字段名称
     */
    public List<Course> searchCourses(String indexName, String filedName, String filedValue) {
        try {
            SearchResponse<Course> searchResponse = client.search(s -> s.index(indexName)
                            .query(q -> q
                                    .match(t -> t
                                            .field(filedName)
                                            .query(filedValue)
                                    )),
                    Course.class);
            List<Hit<Course>> hitList = searchResponse.hits().hits();
            List<Course> courseList = new ArrayList<>();
            for (Hit<Course> mapHit : hitList) {
                courseList.add(mapHit.source());
            }
            return courseList;
        } catch (IOException e) {
            throw new EsException("【查询 -> 失败】从es中查询分析后的日志出错，错误信息为：" + e.getMessage());
        }
    }
}